<?php
/**
 * JoiGallery - image gallery component for Joostina
 *
 * Image Classes
 *
 * @version 1.0 alpha 3
 * @package JoiGallery
 * @filename image.class.php
 * @author JoostinaTeam
 * @copyright (C) 2008-2009 Joostina Team
 * @license see license.txt
 *
 **/

defined('_VALID_MOS') or die();

class joiGalleryUploader {

    var $type = null;
    var $allow_img_formats = null;
    var $upload_file = null;
    var $tempdir = null;
    var $error = 'Unknown error';
    
    function joiGalleryUploader($type = 'upload_single', $allow_img_formats = null) {
	$this->type = $type;
	if (!$allow_img_formats) {
	    $this->allow_img_formats = array('image/pjpeg', 'image/jpeg', 'image/jpg', 'image/png', 'image/x-png', 'image/gif');
	}
	else {
	    $this->allow_img_formats = $allow_img_formats;
	}
    }
    
    function process_upload($dir = '') {
	switch ($this->type) {

	    case 'upload_single':
	    default:
		return $this->upload_single();
		break;

	    case 'upload_zip_archive':
		return $this->upload_zip_archive();
		break;

	    case 'upload_directory':
		return $this->upload_directory($dir);
		break;

	}
    }
    
    function upload_single() {

	if ($_FILES['photo']['error'] > 0) {
	    switch ($_FILES['photo']['error']) {

		case UPLOAD_ERR_INI_SIZE:
		    $error = 'The uploaded file exceeds the upload_max_filesize directive in php.ini.';
		    break;

		case UPLOAD_ERR_FORM_SIZE:
		    $error = 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.';
		    break;

		case UPLOAD_ERR_PARTIAL:
		    $error = 'The uploaded file was only partially uploaded.';
		    break;

		case UPLOAD_ERR_NO_FILE:
		    $error = 'No file was uploaded.';
		    break;

		case UPLOAD_ERR_NO_TMP_DIR:
		    $error = 'Missing a temporary folder.';
		    break;

		case UPLOAD_ERR_CANT_WRITE:
		    $error = 'Failed to write file to disk.';
		    break;

		case UPLOAD_ERR_EXTENSION:
		    $error = 'File upload stopped by extension.';
		    break;

		default:
		    $error = 'Unknown error';
		    break;
	    }

	    $this->error = "Upload error for file " . $_FILES['photo']['name'] . ".\\nError message: " . $error;
	    return false;

	}

	if (!in_array($_FILES['photo']['type'], $this->allow_img_formats)) {
	    $this->error = "Photo type: " . $_FILES['photo']['type'] . " is an unknown photo type";
	    return false;
	}

	return true;
    }
    
    function upload_zip_archive() {

	$mainframe = & mosMainFrame::getInstance();

	if ($_FILES['zip']['error'] > 0) {
	    switch ($_FILES['zip']['error']) {
		case UPLOAD_ERR_INI_SIZE:
		    $err_msg = 'The uploaded file exceeds the upload_max_filesize directive in php.ini.';
		    break;
		case UPLOAD_ERR_FORM_SIZE:
		    $err_msg = 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.';
		    break;
		case UPLOAD_ERR_PARTIAL:
		    $err_msg = 'The uploaded file was only partially uploaded.';
		    break;
		case UPLOAD_ERR_NO_FILE:
		    $err_msg = 'No file was uploaded.';
		    break;
		case UPLOAD_ERR_NO_TMP_DIR:
		    $err_msg = 'Missing a temporary folder.';
		    break;
		case UPLOAD_ERR_CANT_WRITE:
		    $err_msg = 'Failed to write file to disk.';
		    break;
		case UPLOAD_ERR_EXTENSION:
		    $err_msg = 'File upload stopped by extension.';
		    break;
		default:
		    $err_msg = 'Unknown error';
		    break;
	    }

	    $this->error = "Upload error for file " . $_FILES['zip']['name'] . ".\\nError message: " . $err_msg;
	    return false;
	}

	if (!$this->check_tmpdir()) {
	    return false;
	}

	$iswin = (substr(PHP_OS, 0, 3) == 'WIN');

	if (eregi('.zip$', $_FILES['zip']['name'])) {

	    require_once ($mainframe->getCfg('absolute_path') . '/administrator/includes/pcl/pclzip.lib.php');
	    require_once ($mainframe->getCfg('absolute_path') . '/administrator/includes/pcl/pclerror.lib.php');

	    $zipfile = new PclZip($_FILES['zip']['tmp_name']);
	    if ($iswin) {
		define('OS_WINDOWS', 1);
	    }
	    else {
		define('OS_WINDOWS', 0);
	    }

	    $ret = $zipfile->extract(PCLZIP_OPT_PATH, $this->tmpdir);
	    if ($ret == 0) {
		$this->error = "Unrecoverable error: " . $zipfile->errorName(true);
		return false;
	    }
	}
	else {
	    require_once ($mainframe->getCfg('absolute_path') . '/includes/Archive/Tar.php');
	    $archive = new Archive_Tar($_FILES['zip']['tmp_name']);
	    $archive->setErrorHandling(PEAR_ERROR_PRINT);

	    if (!$archive->extractModify($this->tmpdir, '')) {
		$this->error = "Extract error";
		return false;
	    }
	}

	return true;
    }
    
    function upload_directory($dir) {

	$mainframe = & mosMainFrame::getInstance();

	if (!$dir|| $dir == $mainframe->getCfg('absolute_path')) {
	    $this->error = 'Не указана директория';
	    return false;
	}

	if (!is_dir($dir)) {
	    $this->error = "Directory $dir does not exist";
	    return false;
	}

	if (!$this->check_tmpdir()) {
	    return false;
	}

	return true;
    }
    
    function check_tmpdir() {
	$mainframe = & mosMainFrame::getInstance();
	$time = time();

	$tmpdir = '';
	if (!mkdir($mainframe->getCfg('absolute_path') . '/media/' . $time)) {
	    if (!mkdir('/tmp/' . $time)) {
		$this->error = "Unable to create temporary extraction directory ";
		return false;
	    }
	    else {
		$tmpdir = '/tmp/' . $time;
	    }
	}
	else {
	    $tmpdir = $mainframe->getCfg('absolute_path') . '/media/' . $time;
	}

	return $this->tmpdir = $tmpdir;
    }
}

class joiGalleryImage {

    var $temp_path = null;
    var $target_dir_original = null;
    var $target_dir_middle = null;
    var $target_dir_thumbs = null;
    var $file_prefix = 'img_';
    var $filename = null;
    var $error = 'Ooops. Unknown error. Sorry.';
    
    function joiGalleryImage($file_options = array()) {
	foreach ($file_options as $key=>$v) {
	    $this->$key = $v;
	}
    }
    
    function upload() {

	//Если директория для категории еще не создана - пытаемся создать её
	if (!self::check_dir($this->target_dir_original)) {
	    $this->error = 'Failed to create  directory for original image';
	    return false;
	}

	//Перемещаем оригинал в целевую директорию
	if (!copy($this->temp_path, $this->target_dir_original . '/' . $this->file_prefix . "_" . $this->filename)) {
	    $this->error = 'Failed to copy  $this->filename to upload path.';
	    return false;
	}

	return true;
    }
    
    function create_middle($resize_options) {

	if (!self::check_dir($this->target_dir_middle)) {
	    $this->error = 'Failed to create  directory for middle image';
	    return false;
	}

	if (!Thumbnail::output($this->temp_path, $this->target_dir_middle . '/' . $this->file_prefix . "_" . $this->filename, $resize_options)) {
	    $this->error = 'Ошибка при создании среднего эскиза изображения.';
	    return false;
	}
	return true;
    }
    
    function create_thumb($resize_options) {

	if (!self::check_dir($this->target_dir_thumbs)) {
	    $this->error = 'Failed to create  directory for thumb image';
	    return false;
	}

	if (!Thumbnail::output($this->temp_path, $this->target_dir_thumbs . '/' . $this->file_prefix . "_" . $this->filename, $resize_options)) {
	    $this->error = 'Ошибка при создании превью изображения.';
	    return false;
	}
	return true;
    }
    
    function check_dir($dir) {
	if (!is_dir($dir)) {
	    if (!mkdir($dir)) {
		return false;
	    }
	    mosChmod($dir, 0777);
	    return true;
	}
	return true;
    }
    
    function isFileIsset($file) {
	global $mosConfig_absolute_path;

	$file_name = $mosConfig_absolute_path . '/' . $file;

	if (file_exists($file_name)) {
	    return true;
	}
	else {
	    return false;
	}
    }
    
    function delFile( & $file) {
	global $mosConfig_absolute_path;
	$file_name = $file->directory . '/' . $file->name;

	if (self::isFileIsset($file_name)) {
	    unlink($mosConfig_absolute_path . '/' . $file_name);
	    return true;
	}
	return false;
    }
}

class water {

    var $fileHandle = null;
    var $newPictureName = null;
    var $pictureInfo = null;
    var $pictureName = null;
    var $prefix = null;
    var $stampInfo = null;
    var $stampName = null;
    var $stampXpos = null;
    var $stampYpos = null;
    
    function __construct($picture, $stamp, $position = 'top_left') {

	$this->pictureName = $picture;
	$this->stampName = $stamp;
	$this->stampPosition = $position;
    }
    
    public static function GDversion() {
	// GD 2.0.28 or newer is recommended version to use
	var_dump(gd_info()); // dump information about your GD version

	return true;
    }
    
    private function openImage($fileName, $type) {

	// open picture with correct image function. Add more types if needed.
	// GIF: http://php.net/manual/en/function.imagecreatefromgif.php
	// JPG/JPEG: http://php.net/manual/en/function.imagecreatefromjpeg.php
	// PNG: http://php.net/manual/en/function.imagecreatefrompng.php
	switch ($type) {

	    case 1: // GIF
		$this->fileHandle = imagecreatefromgif($fileName);
		break;// case 1

	    case 2: // JPG/JPEG
		$this->fileHandle = imagecreatefromjpeg($fileName);
		break;// case 2

	    case 3: // PNG
		$this->fileHandle = imagecreatefrompng($fileName);
		break;// case 3

	    default:
		die('Unsupported filetype: ' . $fileName);
	}

	return $this->fileHandle;
    }
    
    public function stampPicture() {

	//Получаем информацию о загружаемом изображении, такую как: ширина, высота, расширение
	// get picture info such as width, height and extension
	// http://php.net/manual/en/function.getimagesize.php
	$this->pictureInfo = getimagesize($this->pictureName) or die('Error getting picture info. Double check file path.');
	$pic_width = $this->pictureInfo[0]; //ширина
	$pic_height = $this->pictureInfo[1]; //высота

	//Информация о ватермарке
	$this->stampInfo = getimagesize($this->stampName) or die('Error getting stamp info. Double check file path.');
	$stamp_width = $this->stampInfo[0]; //ширина
	$stamp_height = $this->stampInfo[1]; //высота

	// open images with class method openImage()
	$this->pictureFile = $this->openImage($this->pictureName, $this->pictureInfo[2]);
	$this->stampFile = $this->openImage($this->stampName, $this->stampInfo[2]);

	// position the stamp
	$this->get_cords($pic_width, $pic_height, $stamp_width, $stamp_height);

	imagealphablending($this->pictureFile, TRUE);

	$finalWaterMarkImage = $this->stampFile;
	$finalWaterMarkWidth = imagesx($finalWaterMarkImage);
	$finalWaterMarkHeight = imagesy($finalWaterMarkImage);

	imagecopy($this->pictureFile,
		$finalWaterMarkImage,
		$this->stampXpos,
		$this->stampYpos,
		0,
		0,
		$finalWaterMarkWidth,
		$finalWaterMarkHeight
	);


	// set a new name for the stamped picture and keep the original picture intact
	$this->newPictureName = $this->pictureName;

	switch ($this->pictureInfo[2]) {

	    case 1: // GIF
		imagegif($this->pictureFile, $this->newPictureName);
		break;// case 1

	    case 2:// JPG/JPEG
		imagejpeg($this->pictureFile, $this->newPictureName);
		break;// case 2

	    case 3: // PNG
		imagepng($this->pictureFile, $this->newPictureName);
		break;// case 3

	    default:
		die('Unsupported filetype: ' . $fileName);

	}

	return true;
    }
    
    function get_cords($pic_width, $pic_height, $stamp_width, $stamp_height) {

	switch ($this->stampPosition) {

	    case 'top_left':
	    default:
		$this->stampXpos = 0;
		$this->stampYpos = 0;
		return;

	    case 'top_right':
		$this->stampXpos = round(($pic_width - $stamp_width), 0);
		$this->stampYpos = 0;
		return;

	    case 'top_center':
		$this->stampXpos = round(($pic_width - $stamp_width) / 2, 0);
		$this->stampYpos = 0;
		return;

	    case 'bottom_left':
		$this->stampXpos = 0;
		$this->stampYpos = $pic_height - $stamp_height;
		return;

	    case 'bottom_right':
		$this->stampXpos = round(($pic_width - $stamp_width), 0);
		$this->stampYpos = $pic_height - $stamp_height;
		return;

	    case 'bottom_center':
		$this->stampXpos = round(($pic_width - $stamp_width) / 2, 0);
		$this->stampYpos = $pic_height - $stamp_height;
		return;

	    case 'middle':
		$this->stampXpos = round(($pic_width - $stamp_width) / 2, 0);
		$this->stampYpos = round(($pic_height - $stamp_height) / 2, 0);
		return;

	}
    }
    
    function destruct() {
	unset($fileHandle, $newPictureName, $pictureInfo, $pictureName, $prefix, $stampInfo, $stampName, $stampXpos, $stampYpos);
    }
}

if (!class_exists('Thumbnail')) {

    /**
     * Maximal scaling
     */
    define('THUMBNAIL_METHOD_SCALE_MAX', 0);

    /**
     * Minimal scaling
     */
    define('THUMBNAIL_METHOD_SCALE_MIN', 1);

    /**
     * Cropping of fragment
     */
    define('THUMBNAIL_METHOD_CROP', 2);

    /**
     * Align constants
     */
    define('THUMBNAIL_ALIGN_CENTER', 0);
    define('THUMBNAIL_ALIGN_LEFT', 1);
    define('THUMBNAIL_ALIGN_RIGHT',  1);
    define('THUMBNAIL_ALIGN_TOP',	- 1);
    define('THUMBNAIL_ALIGN_BOTTOM', + 1);

// }}}
// {{{
    class Thumbnail {

// {{{
	/**
	 * Create a GD image resource from given input.
	 *
	 * This method tried to detect what the input, if it is a file the
	 * createImageFromFile will be called, otherwise createImageFromString().
	 *
	 * @param  mixed $input The input for creating an image resource. The value
	 *					  may a string of filename, string of image data or
	 *					  GD image resource.
	 *
	 * @return resource	 An GD image resource on success or false
	 * @access public
	 * @static
	 * @see	Thumbnail::imageCreateFromFile(), Thumbnail::imageCreateFromString()
	 */
	function imageCreate($input) {
	    if (is_file($input)) {
		return Thumbnail::imageCreateFromFile($input);
	    } else if (is_string($input)) {
		return Thumbnail::imageCreateFromString($input);
	    } else {
		return $input;
	    }
	}

// }}}
	// {{{
	/**
	 * Create a GD image resource from file (JPEG, PNG support).
	 *
	 * @param  string $filename The image filename.
	 *
	 * @return mixed			GD image resource on success, FALSE on failure.
	 * @access public
	 * @static
	 */
	function imageCreateFromFile($filename) {
	    if (!is_file($filename) || !is_readable($filename)) {
		user_error('Unable to open file "' . $filename . '"', E_USER_NOTICE);
		return false;
	    }

	    // determine image format
	    list(,, $type) = getimagesize($filename);

	    switch ($type) {
		case IMAGETYPE_JPEG:
		    return imagecreatefromjpeg($filename);
		    break;
		case IMAGETYPE_PNG:
		    return imagecreatefrompng($filename);
		    break;
	    }
	    user_error('Unsupport image type', E_USER_NOTICE);
	    return false;
	}

// }}}
	// {{{
	/**
	 * Create a GD image resource from a string data.
	 *
	 * @param  string $string The string image data.
	 *
	 * @return mixed		  GD image resource on success, FALSE on failure.
	 * @access public
	 * @static
	 */
	function imageCreateFromString($string) {
	    if (!is_string($string) || empty($string)) {
		user_error('Invalid image value in string', E_USER_NOTICE);
		return false;
	    }

	    return imagecreatefromstring($string);
	}

// }}}
	// {{{
	/**
	 * Display rendered image (send it to browser or to file).
	 * This method is a common implementation to render and output an image.
	 * The method calls the render() method automatically and outputs the
	 * image to the browser or to the file.
	 *
	 * @param  mixed   $input   Destination image, a filename or an image string data or a GD image resource
	 * @param  array   $options Thumbnail options
	 *		 <pre>
	 *		 width   int	Width of thumbnail
	 *		 height  int	Height of thumbnail
	 *		 percent number Size of thumbnail per size of original image
	 *		 method  int	Method of thumbnail creating
	 *		 halign  int	Horizontal align
	 *		 valign  int	Vertical align
	 *		 </pre>
	 *
	 * @return boolean		  TRUE on success or FALSE on failure.
	 * @access public
	 */
	function output($input, $output = null, $options = array()) {
	    // Load source file and render image
	    $renderImage = Thumbnail::render($input, $options);
	    if (!$renderImage) {
		user_error('Error rendering image', E_USER_NOTICE);
		return false;
	    }

	    // Set output image type
	    // By default PNG image
	    $type = isset($options['type']) ? $options['type'] : IMAGETYPE_PNG;

	    // Before output to browsers send appropriate headers
	    if (empty($output)) {
		$content_type = image_type_to_mime_type($type);
		if (!headers_sent()) {
		    header('Content-Type: ' . $content_type);
		} else {
		    user_error('Headers have already been sent. Could not display image.', E_USER_NOTICE);
		    return false;
		}
	    }

	    // Define outputing function
	    switch ($type) {
		case IMAGETYPE_PNG:
		    $result = empty($output) ? imagepng($renderImage) : imagepng($renderImage, $output);
		    break;
		case IMAGETYPE_JPEG:
		    $result = empty($output) ? imagejpeg($renderImage) : imagejpeg($renderImage, $output);
		    break;
		default:
		    user_error('Image type ' . $content_type . ' not supported by PHP', E_USER_NOTICE);
		    return false;
	    }

	    // Output image (to browser or to file)
	    if (!$result) {
		user_error('Error output image', E_USER_NOTICE);
		return false;
	    }

	    // Free a memory from the target image
	    imagedestroy($renderImage);

	    return true;
	}

// }}}
	// {{{ render()
	/**
	 * Draw thumbnail result to resource.
	 *
	 * @param  mixed   $input   Destination image, a filename or an image string data or a GD image resource
	 * @param  array   $options Thumbnail options
	 *
	 * @return boolean TRUE on success or FALSE on failure.
	 * @access public
	 * @see	Thumbnail::output()
	 */
	function render($input, $options = array()) {
	    // Create the source image
	    $sourceImage = Thumbnail::imageCreate($input);
	    if (!is_resource($sourceImage)) {
		user_error('Invalid image resource', E_USER_NOTICE);
		return false;
	    }
	    $sourceWidth = imagesx($sourceImage);
	    $sourceHeight = imagesy($sourceImage);

	    // Set default options
	    static $defOptions = array(
	'width'   => 150,
	'height'  => 150,
	'method'  => THUMBNAIL_METHOD_SCALE_MAX,
	'percent' => 0,
	'halign'  => THUMBNAIL_ALIGN_CENTER,
	'valign'  => THUMBNAIL_ALIGN_CENTER,
	    );
	    foreach ($defOptions as $k => $v) {
		if (!isset($options[$k])) {
		    $options[$k] = $v;
		}
	    }

	    // Estimate a rectangular portion of the source image and a size of the target image
	    if ($options['method'] == THUMBNAIL_METHOD_CROP) {
		if ($options['percent']) {
		    $W = floor($options['percent'] * $sourceWidth);
		    $H = floor($options['percent'] * $sourceHeight);
		} else {
		    $W = $options['width'];
		    $H = $options['height'];
		}

		$width = $W;
		$height = $H;

		$Y = Thumbnail::_coord($options['valign'], $sourceHeight, $H);
		$X = Thumbnail::_coord($options['halign'], $sourceWidth, $W);
	    } else {
		$X = 0;
		$Y = 0;

		$W = $sourceWidth;
		$H = $sourceHeight;

		if ($options['percent']) {
		    $width = floor($options['percent'] * $W);
		    $height = floor($options['percent'] * $H);
		} else {
		    $width = $options['width'];
		    $height = $options['height'];

		    if ($options['method'] == THUMBNAIL_METHOD_SCALE_MIN) {
			$Ww = $W / $width;
			$Hh = $H / $height;
			if ($Ww > $Hh) {
			    $W = floor($width * $Hh);
			    $X = Thumbnail::_coord($options['halign'], $sourceWidth, $W);
			} else {
			    $H = floor($height * $Ww);
			    $Y = Thumbnail::_coord($options['valign'], $sourceHeight, $H);
			}
		    } else {
			if ($H > $W) {
			    $width = floor($height / $H * $W);
			} else {
			    $height = floor($width / $W * $H);
			}
		    }
		}
	    }

	    // Create the target image
	    if (function_exists('imagecreatetruecolor')) {
		$targetImage = imagecreatetruecolor($width, $height);
	    } else {
		$targetImage = imagecreate($width, $height);
	    }
	    if (!is_resource($targetImage)) {
		user_error('Cannot initialize new GD image stream', E_USER_NOTICE);
		return false;
	    }

	    // Copy the source image to the target image
	    if ($options['method'] == THUMBNAIL_METHOD_CROP) {
		$result = imagecopy($targetImage, $sourceImage, 0, 0, $X, $Y, $W, $H);
	    } elseif (function_exists('imagecopyresampled')) {
		$result = imagecopyresampled($targetImage, $sourceImage, 0, 0, $X, $Y, $width, $height, $W, $H);
	    } else {
		$result = imagecopyresized($targetImage, $sourceImage, 0, 0, $X, $Y, $width, $height, $W, $H);
	    }
	    if (!$result) {
		user_error('Cannot resize image', E_USER_NOTICE);
		return false;
	    }

	    // Free a memory from the source image
	    imagedestroy($sourceImage);

	    // Save the resulting thumbnail
	    return $targetImage;
	}

// }}}
	// {{{ _coord()
	function _coord($align, $param, $src) {
	    if ($align < THUMBNAIL_ALIGN_CENTER) {
		$result = 0;
	    } elseif ($align > THUMBNAIL_ALIGN_CENTER) {
		$result = $param - $src;
	    } else {
		$result = ($param - $src) >> 1;
	    }
	    return $result;
	}
    }

}